// CommWithServer.cpp: implementation of the CommWithServer class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CommWithServer.h"
#include "ClientSocket.h"
#include "ClientSocketThread.h"

#include "CommonLib\Packet.h"
#include "CommonLib\Guard.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CommWithServer::CommWithServer():
m_pClientSocketThread(NULL),
m_pWnd(NULL),
m_nPacketId(0),
m_bConnected(false)
{

}

CommWithServer::~CommWithServer()
{
  if (m_pClientSocketThread) m_pClientSocketThread->DeleteInstance();   
}

CommWithServer * CommWithServer::m_pInstance = NULL;

CommWithServer * CommWithServer::Instance()
{
  if (m_pInstance == NULL)
  {
    m_pInstance = new CommWithServer;
  }

  return m_pInstance;
}

void CommWithServer::DeleteInstance()
{
  if (m_pInstance) delete m_pInstance;

  m_pInstance = NULL;
}

bool CommWithServer::Connect(const CString& strServerIPAddress, int nPort)
{
	if (m_bConnected) return true;
	
	bool bRet = false;
	
	/*
	if (m_pClientSocketThread) m_pClientSocketThread->DeleteInstance();
	m_pClientSocketThread = (CClientSocketThread *)AfxBeginThread(RUNTIME_CLASS(CClientSocketThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
	ASSERT(m_pClientSocketThread);
	
	  m_pClientSocketThread->m_strServerIPAddress = strServerIPAddress;
	  m_pClientSocketThread->m_nPort = nPort;
	  m_eventThread.ResetEvent();
	  m_pClientSocketThread->ResumeThread();
	  CSingleLock singlelock(&m_eventThread, TRUE);
	*/
	m_eventThread.ResetEvent();
	if (!m_pClientSocketThread) {
		m_pClientSocketThread = (CClientSocketThread *)AfxBeginThread(RUNTIME_CLASS(CClientSocketThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
		ASSERT(m_pClientSocketThread);
		
		m_pClientSocketThread->m_strServerIPAddress = strServerIPAddress;
		m_pClientSocketThread->m_nPort = nPort;
		
	}else{
		m_pClientSocketThread->SuspendThread();
		
		if(!m_pClientSocketThread->InitInstance()){
			return false;
		}
		
	}
	m_pClientSocketThread->ResumeThread();
	CSingleLock singlelock(&m_eventThread, TRUE);
	
	DWORD dwTickCount = GetTickCount();
	while (!m_bConnected && GetTickCount()-dwTickCount < 5000)
	{
		MSG msg;
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (!(msg.message == WM_QUIT || msg.message == WM_CLOSE || msg.message == WM_COMMAND && msg.wParam == ID_APP_EXIT)) 
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
	}
	if (m_bConnected)
	{
		bRet = true;
	}
	
	return bRet;
}

bool CommWithServer::Disconnect()
{
  if (!m_bConnected)
  {
    if (m_pClientSocketThread) 
    {
      m_pClientSocketThread->DeleteInstance();
      m_pClientSocketThread = NULL;
    }
    return false;
  }

  m_bConnected = false;
  m_pClientSocketThread->PostThreadMessage(WM_APP_EXITSOCKETTHREAD, 0, 0);
  VERIFY(WaitForSingleObject(m_pClientSocketThread->m_hThread, INFINITE) == WAIT_OBJECT_0);
  m_pClientSocketThread->DeleteInstance();
  m_pClientSocketThread = NULL;

  return true;
}

long CommWithServer::SendRequest(const Packet& packet)
{
  if (!m_bConnected) return 0;
  
  Packet * pPacket = new Packet;
  *pPacket = packet;
  if (m_nPacketId == 0x7fffffff) m_nPacketId = 0;
  m_nPacketId++;
  pPacket->m_dwId = m_nPacketId;

  m_pClientSocketThread->AddPacketToSend(pPacket);

  return m_nPacketId;
}

long CommWithServer::SendRequest(LPBYTE lpbyData, DWORD dwDataSize)
{
  Packet packet(dwDataSize, lpbyData);

  return SendRequest(packet);
}

void CommWithServer::SendBroadcast(const Packet& packet)
{
  if (!m_bConnected) return;

  Packet * pPacket = new Packet;
  *pPacket = packet;
  pPacket->m_dwId = 0;

  m_pClientSocketThread->AddPacketToSend(pPacket);
}

bool CommWithServer::Receive(Packet& packet, bool& bCancel, DWORD dwTimeOut)
{
	if (!m_bConnected) return false;
	
	bool bRet = false;
	
	CTime timeStart = CTime::GetCurrentTime();
	
	while (!bCancel)
	{
		Packet * pPacket = m_pClientSocketThread->TakePacketReceived(packet.m_dwId);
		if (pPacket)
		{
			packet = *pPacket;
			delete pPacket;
			bRet = true;
			break;
		}
		
		CTimeSpan timespan = CTime::GetCurrentTime()-timeStart;
		if (timespan.GetTotalSeconds() > dwTimeOut)
		{
			TRACE0("Time out!\n");
			Packet * pPacket = m_pClientSocketThread->TakePacketToSend(packet.m_dwId);
			delete pPacket;
			break;
		}
	}
	
	return bRet;
}

bool CommWithServer::Receive(DWORD dwId, LPBYTE lpbyData, DWORD& dwDataSize, bool& bCancel, DWORD dwTimeOut)
{
  if (!m_bConnected) return false;
  
  bool bRet = false;

  CTime timeStart = CTime::GetCurrentTime();

  while (!bCancel)
  {
    DWORD dwPacketSize = m_pClientSocketThread->TakeSizeOfPacketReceived(dwId);
    if (dwPacketSize != 0)
    {
      if (dwPacketSize <= dwDataSize)
      {
        Packet * pPacket = m_pClientSocketThread->TakePacketReceived(dwId);
        ASSERT(pPacket);
        memcpy(lpbyData, pPacket->GetpData(), dwPacketSize);
        delete pPacket;
        bRet = true;
      }
      dwDataSize = dwPacketSize;
      break;
    }

    CTimeSpan timespan = CTime::GetCurrentTime()-timeStart;
    if (timespan.GetTotalSeconds() > dwTimeOut)
    {
      TRACE0("Time out!\n");
      Packet * pPacket = m_pClientSocketThread->TakePacketToSend(dwId);
      delete pPacket;
      break;
    }
  }

  if (bCancel)
  {
    dwDataSize = 0;
    bRet = true;
  }

  return bRet;
}

bool CommWithServer::Receive(Packet& packet, DWORD dwTimeOut)
{
  bool bCancel = false;

  return Receive(packet, bCancel, dwTimeOut);
}

bool CommWithServer::PostNotification(UINT message)
{
  if (m_pWnd && IsWindow(m_pWnd->GetSafeHwnd()))
  {
    return m_pWnd->PostMessage(message);
  }
  else 
  {
    return false;
  }
}
